package com.handlemvc.springmvc.servlet;

import com.alibaba.fastjson.JSON;
import com.handlemvc.springmvc.annotation.Controller;
import com.handlemvc.springmvc.annotation.RequestMapping;
import com.handlemvc.springmvc.annotation.ResponseBody;
import com.handlemvc.springmvc.constant.VistMethod;
import com.handlemvc.springmvc.context.WebApplicationContext;
import com.handlemvc.springmvc.context.beanfactory.InitializingBean;
import com.handlemvc.springmvc.handler.MyMvcHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 自定义前端控制器
 * @author ：tiger.
 * @date ：2021/10/13 00:07
 */
@Slf4j
public class DispatcherServlet extends HttpServlet implements InitializingBean {

	//制定springmvc容器
	private WebApplicationContext webApplicationContext;

	private ConcurrentHashMap<String, MyMvcHandler> handlers = new ConcurrentHashMap<>();

	@Override
	public void init() throws ServletException {
		//1、加载初始化参数
		String contextConfigLocation = this.getServletConfig().getInitParameter("contextConfigLocation");
		//2、创建springmvc容器
		webApplicationContext = new WebApplicationContext(contextConfigLocation);
		//3、初始化操作
		webApplicationContext.onRefresh();
		//4、初始化请求映射关系 /findUser --> 拦截器方法
		this.afterPropertiesSet();
	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		this.doDispatcher(req, resp);
	}

	private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws IOException {

		MyMvcHandler myMvcHandler = this.getHandler(req);
		try {
			if(myMvcHandler == null){
				resp.setHeader("ContentType","text/html:charset=utf-8");
				resp.getWriter().print("<html lang=\"en\">\n" + "<head>\n" + "    <meta charset=\"UTF-8\"></head>\n" + "<body><h1>404 您请求的页面不见了！<h1></body>\n" + "</html>");
			}else{
				//假设进行请求转发
				doResponse(myMvcHandler,req,resp);
			}
		}catch (Exception e){
			log.error("请求失败");
			resp.setHeader("ContentType","text/html:charset=utf-8");
			resp.getWriter().print("<html lang=\"en\">\n" + "<head>\n" + "    <meta charset=\"UTF-8\"></head>\n" + "<body><h1>404 您请求的页面错误！<h1></body>\n" + "</html>");
		}

	}

	private void doResponse(MyMvcHandler myMvcHandler, HttpServletRequest req, HttpServletResponse resp) throws Exception {
		List<Object> paramValues = getParamValues(myMvcHandler, req);
		Object result = myMvcHandler.getMethod().invoke(myMvcHandler.getController(),paramValues.toArray());
		if(result instanceof String){
			String page = String.valueOf(result);
			if(StringUtils.contains(page,":")){
				String[] split = StringUtils.split(page, ":");
				String viewType = split[0];
				String viewPage = split[1];
				if("forward".equals(viewType)){
					//forward:s/success.jsp
					req.getRequestDispatcher(viewPage).forward(req, resp);
					return;
				}else{
					//redirect:/user.jsp
					resp.sendRedirect(viewPage);
					return;
				}
			}
			//按照返回结果进行请求跳转
			req.getRequestDispatcher(page).forward(req, resp);
		}else {
			Method method = myMvcHandler.getMethod();
			//返回json对象
			if (method.isAnnotationPresent(ResponseBody.class)) {
				String jsonBody = JSON.toJSONString(result);
				PrintWriter writer = resp.getWriter();
				resp.setContentType("text/html:charset=utf-8");
				writer.print(jsonBody);
				writer.close();
			}
		}
	}

	private List<Object> getParamValues(MyMvcHandler myMvcHandler, HttpServletRequest req) {
		Map<String, String[]> parameterMap = req.getParameterMap();
		Parameter[] parameters = myMvcHandler.getMethod().getParameters();
		List<Object> paramValues = Arrays.stream(parameters).map(Parameter::getName)
				.map(parameterMap::get)
				.map(args->{
					if (args.getClass().isArray()){
						Object[] objects = (Object[])args;
						if (objects.length == 1){
							return objects[0];
						}
						return objects;
					}
					return args;
				})
				.collect(Collectors.toList());
		return paramValues;
	}

	private MyMvcHandler getHandler(HttpServletRequest req) {
		String url = req.getRequestURI();
		return handlers.get(url);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		this.doGet(req, resp);
	}

	@Override
	public void afterPropertiesSet() {
		//初始化请求映射关系
		webApplicationContext.getBeans().forEach((beanName,bean)->{
			if(!bean.getClass().isAnnotationPresent(Controller.class)){
				return;
			}
			Method[] declaredMethods = bean.getClass().getDeclaredMethods();
			for (Method declaredMethod : declaredMethods) {
				if (!declaredMethod.isAnnotationPresent(RequestMapping.class)) {
					continue;
				}
				RequestMapping annotation = declaredMethod.getAnnotation(RequestMapping.class);
				String value = annotation.value();
				if(StringUtils.isNotBlank(value)){
					handlers.put(value, new MyMvcHandler(value,bean,declaredMethod));
				}
			}
		});
	}
}
